<template>
  <div class="markdown-container">
    <div class="markdown-body" v-html="compiledMarkdown" />
  </div>
</template>

<script setup lang="ts">
import {computed} from 'vue'
import type {Options} from 'markdown-it'
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import DOMPurify from 'dompurify'
import {full as MarkdownItEmoji} from 'markdown-it-emoji'
import 'highlight.js/styles/atom-one-dark.css'
// import 'github-markdown-css'
import '@/assets/my-markdown.css'
import MarkdownItSub from 'markdown-it-sub'
import MarkdownItSup from 'markdown-it-sup'
import MarkdownItFootnote from 'markdown-it-footnote'
import MarkdownItMark from 'markdown-it-mark'
import MarkdownItIns from 'markdown-it-ins'
import MarkdownItContainer from 'markdown-it-container'
import MarkdownItAbbr from 'markdown-it-abbr'
import MarkdownItDefList from 'markdown-it-deflist'
import MarkdownItTaskLists from 'markdown-it-task-lists'
import MarkdownItKatex from '@iktakahiro/markdown-it-katex'
import MarkdownItAnchor from 'markdown-it-anchor'
import MarkdownItTocDoneRight, {TocAst} from 'markdown-it-toc-done-right'
import uslug from 'uslug'
import {TreeNode} from "@/types/treeType.ts";

const props = defineProps({
  content: {
    type: String,
    default: ''
  }
})
const emit = defineEmits(['getToc'])

const md = new MarkdownIt({
  html: true,
  linkify: true,
  typographer: true,
  highlight: (str: string, lang: string) => {
    // 此处判断是否有添加代码语言
    if (lang && hljs.getLanguage(lang)) {
      try {
        // 得到经过highlight.js之后的html代码
        let html = hljs.highlight(str, {language: lang,ignoreIllegals: true}).value
        // 添加代码语言
        html += '<b class="name">' + lang + '</b>'
        return '<pre class="hljs"><code>' +
            html +
            '</code></pre>'
      } catch (__) {}
    }
    // 如果 language 获取失败，返回带```标签的字符串
    return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
  }
} as Options).use(MarkdownItEmoji)
    .use(MarkdownItSub)
    .use(MarkdownItSup)
    .use(MarkdownItFootnote)
    .use(MarkdownItMark)
    .use(MarkdownItIns)
    .use(MarkdownItContainer, 'warning')
    .use(MarkdownItAbbr)
    .use(MarkdownItDefList)
    .use(MarkdownItTaskLists)
    .use(MarkdownItKatex)
    .use(MarkdownItAnchor,{
      // permalink: true,
      // permalinkBefore: true,
      // permalinkSymbol: '§',
      slugify: (str: string) => uslug(str),
    })
    .use(MarkdownItTocDoneRight,{
      slugify: (str: string) => uslug(str),
      // @ts-ignore
      callback: (html: string, ast: TocAst) => {
        // ast 包含目录的层级结构及锚点信息
        emit('getToc', parseAST(ast))
      }
    })

const compiledMarkdown = computed(() => {
  try {
    return DOMPurify.sanitize(md.render(props.content))
  } catch (error) {
    console.error('Markdown渲染错误:', error)
    return '<div class="markdown-error">内容渲染失败</div>'
  }
})

function parseAST(toc: TocAst): TreeNode {
  return {
    id: uslug(toc.n),
    title: toc.n,
    children: toc.c?.map(child => parseAST(child)),
    expanded: false // 默认折叠
  }
}
</script>

<style lang="less" >
/* scoped 会影响子的style 需要去掉 */
//@import 'highlight.js/styles/atom-one-dark.css';
//@import 'highlight.js/styles/github-dark.css';
// github-markdown-css 针对的是 class=`markdown-body`,如果主容器不是markdown-body的，需要修改
//@import 'github-markdown-css';

.markdown-container {
  width: 100%;
}

// 添加行号样式
pre.hljs {
  padding: 8px 2px;
  border-radius: 5px;
  position: relative;
  b.name {
    position: absolute;
    top: 2px;
    right: 12px;
    z-index: 10;
    color: #999;
    pointer-events: none;
  }
}

.markdown-error {
  color: #dc3545;
  padding: 1rem;
  border: 1px solid #dc3545;
  border-radius: 4px;
}
</style>
